package com.atguigu.daijia.order.testLock;

import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.UUID;

/*
@description:测试分布式锁
@ClassName TestServiceImpl
@author chen
@create 2024-09-10 21:52
@Version 1.0
*/
@Service
public class TestServiceImpl implements TestService{

    @Resource
    private StringRedisTemplate redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public void testLuaLock()
    {
        // 从redis里面获取数据
        String uuid = UUID.randomUUID().toString();
        // 1 获取当前锁  setnx  + 设置过期时间
        Boolean ifAbsent =
                redisTemplate.opsForValue()
                        .setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
        if(Boolean.TRUE.equals(ifAbsent))
        {
            try
            {
                //获取锁成功，执行业务代码
                // 1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
                String value = redisTemplate.opsForValue().get("num");
                // 2.如果值为空则非法直接返回即可
                if (StringUtils.isBlank(value))
                {
                    return;
                }
                // 3.对num值进行自增加一
                int num = Integer.parseInt(value);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));
            }
            finally
            {
                // 4.业务逻辑执行完成释放锁,要保证当前释放的锁和自己当时拿到的锁是一把锁
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                // lua脚本
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                redisScript.setScriptText(script);
                //设置返回结果
                redisScript.setResultType(Long.class);
                Long lock = redisTemplate.execute(redisScript, Collections.singletonList("lock"), uuid);
            }
        }
        else // 获取锁失败,自旋
        {
            try
            {
                Thread.sleep(100);
                this.testLuaLock();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void testRedissonLock() {
        //1 通过redisson创建锁对象
        RLock lock = redissonClient.getLock("lock");

        // 获取到锁，锁过期时间10s
         lock.lock(10,TimeUnit.SECONDS);

        try {
            //3 编写业务代码
            //1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
            String value = redisTemplate.opsForValue().get("num");
            //2.如果值为空则非法直接返回即可
            if (StringUtils.isBlank(value)) {
                return;
            }
            //3.对num值进行自增加一
            int num = Integer.parseInt(value);
            redisTemplate.opsForValue().set("num", String.valueOf(++num));
        }finally {
            // 4 释放锁
            lock.unlock();
        }

    }
}
